#ifndef B2D_EXCEPTION_H
#define B2D_EXCEPTION_H

#include "Prerequisites.h"
#include <exception>

NS_B2D_BEGIN


////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Base class for exceptions.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API Exception
	: public std::exception
{
public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Types of build-in exceptions
	////////////////////////////////////////////////////////////////////////////////////////////////////
	enum
	{
		UNKNOWN_EXCEPTION,
		INVALID_ARGUMENT_EXCEPTION,
		NULL_ALRGUMENT_EXCEPTION,
		INDEX_OUT_OF_RANGE_EXCEPTION,
		BUFFER_OVERFLOW_EXCEPTION,
		NOT_SUPPORTED_EXCEPTION,
		NOT_IMPLEMENTED_EXCEPTION,
		INTERNAL_ERROR_EXCEPTION,
		DUPLICATE_ITEM_EXCEPTION,
		PARSING_EXCEPTION,
		GRAPHICS_API_EXCEPTION,
		SYSTEM_EXCEPTION,
		IO_EXCEPTION,
		INVALID_OPERATION_EXCEPTION
	};

protected:
	int mType;
	string mString;
	string mFile;
	int mLine;
	string mFunction;

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Constructor.
	///
	/// \param	type		The type of exception.
	/// \param	str			The exception string.
	/// \param	file		The file from which it was thrown.
	/// \param	line		The line from which it was thrown.
	/// \param	function	The function from which it was thrown.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	Exception(int type, const string& str, const char* file, int line, const char* function)
		: mType(type)
		, mString(str)
		, mFile(file)
		, mLine(line)
		, mFunction(function)
	{
	}
	~Exception(void) throw() { }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get type (error code) of the exception.
	///
	/// \return	The type of exception.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	int getType(void) const { return mType; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get string description of the exception.
	///
	/// \return	Exception description.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const string& getString(void) const { return mString; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get file that threw the exception.
	///
	/// \return	The file name.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const string& getFile(void) const { return mFile; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get line that threw the exception.
	///
	/// \return	The line number.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const int getLine(void) const { return mLine; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get function that threw the exception.
	///
	/// \return	The function name.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	const string& getFunction(void) const { return mFunction; }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get name of the exception type.
	///
	/// \return	The type name.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual string getTypeName(void) const { return "Unknown Exception"; }

	
	const char* what() const throw() { return mString.c_str(); }
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Utility macro for defining default exceptions.
///
/// \param	classname	The exception class name.
/// \param	errorcode	The error code.
/// \param	errorname	The error name.
////////////////////////////////////////////////////////////////////////////////////////////////////
#define DEFINE_DEFAULT_EXCEPTION(classname, errorcode, errorname) \
class B2D_API classname \
	: public Exception \
{ \
public: \
	classname(const string& str, const char* file, int line, const char* function) \
		: Exception(errorcode, str, file, line, function) { } \
	virtual string getTypeName(void) const { return errorname; } \
};


DEFINE_DEFAULT_EXCEPTION(UnknownException, Exception::UNKNOWN_EXCEPTION, "Unknown Exception")
DEFINE_DEFAULT_EXCEPTION(InvalidArgumentException, Exception::INVALID_ARGUMENT_EXCEPTION, "Invalid Argument Exception")
DEFINE_DEFAULT_EXCEPTION(NullArgumentException, Exception::NULL_ALRGUMENT_EXCEPTION, "Null Argument Exception")
DEFINE_DEFAULT_EXCEPTION(IndexOutOfRangeException, Exception::INDEX_OUT_OF_RANGE_EXCEPTION, "Index Out Of Range Exception")
DEFINE_DEFAULT_EXCEPTION(BufferOverflowException, Exception::BUFFER_OVERFLOW_EXCEPTION, "Buffer Overflow Exception")
DEFINE_DEFAULT_EXCEPTION(NotSupportedException, Exception::NOT_SUPPORTED_EXCEPTION, "Not Supported Exception")
DEFINE_DEFAULT_EXCEPTION(NotImplementedException, Exception::NOT_IMPLEMENTED_EXCEPTION, "Not Implemented Exception")
DEFINE_DEFAULT_EXCEPTION(InternalErrorException, Exception::INTERNAL_ERROR_EXCEPTION, "Internal Error Exception")
DEFINE_DEFAULT_EXCEPTION(DuplicateItemException, Exception::DUPLICATE_ITEM_EXCEPTION, "Duplicate Item Exception")
DEFINE_DEFAULT_EXCEPTION(ParsingException, Exception::PARSING_EXCEPTION, "Parsing Exception")
DEFINE_DEFAULT_EXCEPTION(GraphicsAPIException, Exception::GRAPHICS_API_EXCEPTION, "Graphics API Exception")
DEFINE_DEFAULT_EXCEPTION(SystemException, Exception::SYSTEM_EXCEPTION, "System Exception")
DEFINE_DEFAULT_EXCEPTION(IOException, Exception::IO_EXCEPTION, "IO Exception")
DEFINE_DEFAULT_EXCEPTION(InvalidOperationException, Exception::INVALID_OPERATION_EXCEPTION, "Invalid Operation Exception")



////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Exception code type.
/// 		Creates a structure for each exception error code.
///
/// \tparam	num	Error code.
////////////////////////////////////////////////////////////////////////////////////////////////////
template <int num>
struct ExceptionCodeType
{
	enum { number = num };
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	A macro that adds a built-in exception to the factory.
///
/// \param	exception	The exception.
/// \param	errorcode	The errorcode.
////////////////////////////////////////////////////////////////////////////////////////////////////
#define ADD_TO_EXCEPTION_FACTORY(exception, errorcode) \
static exception Create( \
	ExceptionCodeType<errorcode> code, \
	const string& str, \
	const char* file, \
	int line, \
	const char* function) \
{ \
	return exception(str, file, line, function); \
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Exception factory.
/// 		Allow automatic creation of exceptions
/// 		ExceptionFactory Idea adapted from Ogre3D
/// 		http://www.ogre3d.org/
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API ExceptionFactory
{
	B2D_STATIC_CLASS(ExceptionFactory)
	
public:
	ADD_TO_EXCEPTION_FACTORY(UnknownException, Exception::UNKNOWN_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(InvalidArgumentException, Exception::INVALID_ARGUMENT_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(NullArgumentException, Exception::NULL_ALRGUMENT_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(IndexOutOfRangeException, Exception::INDEX_OUT_OF_RANGE_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(BufferOverflowException, Exception::BUFFER_OVERFLOW_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(NotSupportedException, Exception::NOT_SUPPORTED_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(NotImplementedException, Exception::NOT_IMPLEMENTED_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(InternalErrorException, Exception::INTERNAL_ERROR_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(DuplicateItemException, Exception::DUPLICATE_ITEM_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(ParsingException, Exception::PARSING_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(GraphicsAPIException, Exception::GRAPHICS_API_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(SystemException, Exception::SYSTEM_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(IOException, Exception::IO_EXCEPTION)
	ADD_TO_EXCEPTION_FACTORY(InvalidOperationException, Exception::INVALID_OPERATION_EXCEPTION)
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Macro for throwing an exception.
/// 		This extracts the file, function and line automatically.
///
/// \param	type	   	The error code.
/// \param	description	The description.
////////////////////////////////////////////////////////////////////////////////////////////////////
#define B2D_THROW(type, description) \
do { \
throw ::b2d::ExceptionFactory::Create( ::b2d::ExceptionCodeType<type>(), description, __FILE__, __LINE__, __FUNCTION__ ); \
} while(0)


NS_B2D_END

#endif